feat(parameters): declare a parameter set per entry of a map - #700
Open
azerupi wants to merge 1 commit into
Open
feat(parameters): declare a parameter set per entry of a map#700azerupi wants to merge 1 commit into
azerupi wants to merge 1 commit into
Conversation
A driver often manages a set of devices whose names only the integrator
knows: the sensors on a robot, the motors on an arm. That was not
expressible. Declaring sensors.front_lidar.rate requires code that already
knows the string "front_lidar", and there was no way to find it out:
overrides live in a private map on the parameter interface and are never
inserted into the parameter map, so use_undeclared_parameters cannot see a
parameter the node has not declared. The choice was to hard-code the
device names or to stop using ROS 2 parameters for those values, giving up
descriptors, ranges, validation, ros2 param list and the parameter
services.
A map field declares a parameter set for each of its entries, with the
entry names recovered from the parameters the node was configured with:
#[derive(ParameterSet, Debug)]
struct SensorHub {
sensors: BTreeMap<String, SensorConfig>,
}
Every leaf is an ordinary parameter, so sensors.front_lidar.rate has the
description and range SensorConfig gives it and can be watched for changes
like any other. Combined with an enum set, the entries do not have to be
the same kind of thing.
The entries are fixed when the map is declared, since that is when the
parameters are declared. A name that turns up later names a parameter that
does not exist. A default value contributes entries too, so a node can
have built-in ones that a parameter file adds to or overrides -- which is
what a default supplied for a whole set taking precedence over a field's
own default is for.
Assisted-by: Claude:claude-opus-5 [Claude Code]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The code in this PR was assisted by Claude Code.
This PR lifts another limitation of the derive. A field of a parameter set can now be a map, declaring one parameter set per entry, with the entry names coming from whoever configures the node.
Problem
If we take the example of the previous PR that allows us to represent different sensors with different parameters through enum sets. We are still limited and can't easily represent different sensor configurations that are defined by the parameter file.
Declaring
devices.front_lidar.rateneeds code that already knows the string"front_lidar". Parameter overrides live in a private map on the parameter interface and are never inserted into the parameter map, so evenuse_undeclared_parameters` cannot see a parameter the node has not declared.That left two options for the user, either hard-code the sensor names in the node, or bypass ROS 2 parameters for those values and give up on the niceties like descriptors, ranges, validation,
ros2 param listand the parameter services.Solution
A map field declares a parameter set for each of its entries. This composes very well with the enum sets from the previous PR, allowing the parameter file to say both which sensors are there and what kind each one is:
Neither the names nor the kinds appear anywhere in the node, and the values still arrive as plain Rust:
Both
BTreeMap<String, S>andHashMap<String, S>work, for anySthat derivesParameterSet. When the entries are all the same shape, thatSis just a struct set. The keys are the names the entries are declared under, so they have to beString.This works again through
DeclareField. A map is just one more implementation of that trait, so nothing in the derive macro knows a map field from any other field and it emits the same line for it as for everything else.Map keys
The names are recovered from the node's parameter overrides.
override_names_under("devices")returns the distinct first path segment of every override underdevices., sodevices.front_lidar.typeanddevices.main_camera.typeyieldfront_lidarandmain_camera.Each name found becomes the prefix of a full parameter set declaration:
devices.front_lidar.widthdoes not exist, because that lidar is not a camera. The descriptions are there, the ranges are enforced, and the parameters can be watched for changes like any other.declare_parametersreturns the handles per entry:The entries are fixed at declaration time
The set of entries is decided when the map is declared, because that is when the parameters are declared. A name that turns up later over
SetParametersnames a parameter that does not exist, and is rejected like any other undeclared parameter. Adding a new entry needs a restart, the same way changing an enum set's tag does.No overrides and no default result in an empty map.